﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;

using ESRI.ArcGIS.Client.Geometry;

namespace GISforTT_API
{
    // Singleton class responsible for creating map frame objects
    public class MapFrameFactory
    {
        /// <summary>
        /// Returns the instance of the MapFrameFactory singleton class
        /// </summary>
        public static MapFrameFactory Instance { get; private set; }

        static MapFrameFactory() { Instance = new MapFrameFactory(); }

        // Private Constructor
        MapFrameFactory() {}
                

        #region creation methods

        /// <summary>
        /// Create a map frame showing the map inside the rectangle defined by the two screen points.
        /// </summary>
        /// <param name="p1">First screen point</param>
        /// <param name="p2">Second screen point</param>
        /// <returns></returns>
        public MapFrame CreateMapFrame(Point p1, Point p2)
        {
            ArcGisMap BackgroundMap = BackgroundMapLayer.Instance.getBackgroundMap();
            MapPoint mapPoint1 = BackgroundMap.Map.ScreenToMap(p1);
            MapPoint mapPoint2 = BackgroundMap.Map.ScreenToMap(p2);

            return CreateMapFrame(mapPoint1, mapPoint2, p1, p2);
        }

        /// <summary>
        /// Adds a new MapFrame to the layer via the
        /// two map points from the map.
        /// 
        /// A MapFrame is created where the extent are defined
        /// by the two points (one for upper left corner and the
        /// other for lower right corner).
        /// 
        /// The position of the MapFrame on the screen is also defined
        /// by the 2 Points value (one for upper left corner and another
        /// for lower right corner).
        /// </summary>
        /// <param name="mapPoint1"></param>
        /// <param name="mapPoint2"></param>
        /// <param name="screenPoint1"></param>
        /// <param name="screenPoint2"></param>
        public MapFrame CreateMapFrame(MapPoint mapPoint1, MapPoint mapPoint2, Point screenPoint1, Point screenPoint2)
        {

            SolidColorBrush randomColor = GetRandomColor();

            ArcGisMap BackgroundMap = BackgroundMapLayer.Instance.getBackgroundMap();

            GhostFrame gFrame = new GhostFrame(BackgroundMap, new Envelope(mapPoint1, mapPoint2), randomColor);
            MapFrame mFrame = new MapFrame(gFrame, mapPoint1, mapPoint2, randomColor);

            /* create new delegates instances and bind them to MapFrame's MapLayer managment methods */
            /*MapLayersManager.MapLayerAddedDelegate delegate1 = new MapLayersManager.MapLayerAddedDelegate(mFrame.MapLayerAdded.ID);
            MapLayersManager.MapLayerDeletedDelegate delegate2 = new MapLayersManager.MapLayerDeletedDelegate(mFrame.MapLayerDeleted);
            MapLayersManager.Instance.MapLayerAdded += delegate1;
            MapLayersManager.Instance.MapLayerDeleted += delegate2;*/

            mFrame.Orientation = 0.0;

            var xDiff = Math.Abs(screenPoint1.X - screenPoint2.X);
            var yDiff = Math.Abs(screenPoint1.Y - screenPoint2.Y);


            if (screenPoint1.X < screenPoint2.X && screenPoint1.Y < screenPoint2.Y)
            {
                mFrame.Orientation = 0.0;
                mFrame.Height = yDiff;
                mFrame.Width = xDiff;
                mFrame.Center = new System.Windows.Point(screenPoint1.X + xDiff / 2, screenPoint1.Y + yDiff / 2);
            }
            else if (screenPoint1.X > screenPoint2.X && screenPoint1.Y < screenPoint2.Y)
            {
                mFrame.Orientation = 90.0;
                mFrame.Height = yDiff;
                mFrame.Width = xDiff;
                mFrame.Center = new System.Windows.Point(screenPoint1.X - xDiff / 2, screenPoint1.Y + yDiff / 2);
            }
            else if (screenPoint1.X > screenPoint2.X && screenPoint1.Y > screenPoint2.Y)
            {
                mFrame.Orientation = 180.0;
                mFrame.Height = yDiff;
                mFrame.Width = xDiff;
                mFrame.Center = new System.Windows.Point(screenPoint1.X - xDiff / 2, screenPoint1.Y - yDiff / 2);
            }
            else if (screenPoint1.X < screenPoint2.X && screenPoint1.Y > screenPoint2.Y)
            {
                mFrame.Orientation = 270.0;
                mFrame.Height = yDiff;
                mFrame.Width = xDiff;
                mFrame.Center = new System.Windows.Point(screenPoint1.X + xDiff / 2, screenPoint1.Y - yDiff / 2);
            }

            return mFrame;
        }

        /// <summary>
        /// Adds a new MapFrame to the layer via the BookmarkPins.
        /// 
        /// The position of the MapFrame (centre) is the same as the related BookmarkPin.  
        /// The orientation of the frame is also defined. The map extent of the frame is the same as the related Bookmark.
        /// </summary>
        /// <param name="bookmark">Related Bookmark</param>
        /// <param name="centerPt">Center point to place the map frame on the screen</param>
        /// <param name="orientation">Orientation of the map frame</param>
        /// <param name="fromBookmarkList">true if the map frame is created from the Bookmark list, false otherwise</param>
        /// <returns></returns>
        public MapFrame CreateMapFrame(Bookmark bookmark, Point centerPt, double orientation, bool fromBookmarkList)
        {

            /* if it is already open, dont open again */
            if (bookmark.MapFrameCurrentlyOpen == true)
            {
                BackgroundMapLayer.Instance.BringMapFrameToFront(bookmark);                
                return null;
            }

            Envelope mapExtent = new Envelope();
            double frameHeight = 270;
            double frameWidth = 270;
            Point frameCenter = centerPt;
            double frameOri = orientation;

            // TODO: Check if this makes a difference or if it is not needed
            if (fromBookmarkList)
            {
                mapExtent = bookmark.MapFrameEnvelope;
                frameOri = orientation + 90;
            }

            if (bookmark.MapFramePreviouslyOpen == true)
            {
                /* Do we really want to open the map frame from it's previous position? */
                //mapExtent = bookmark.MapFrameEnvelope;
                //frameHeight = bookmark.MapFrameHeight;
                //frameWidth = bookmark.MapFrameWidth;
                //frameCenter = bookmark.MapFrameCenter;
                //frameOri = bookmark.MapFrameOrientation;
            }
            else
            {
                mapExtent = bookmark.MapFrameEnvelope;
                //frameHeight = height;
                //frameWidth = width;
                frameCenter = centerPt;
            }

            SolidColorBrush randomColor = GetRandomColor();

            ArcGisMap BackgroundMap = BackgroundMapLayer.Instance.getBackgroundMap();

            GhostFrame gFrame = new GhostFrame(BackgroundMap, mapExtent, randomColor);
            MapFrame mFrame = new MapFrame(gFrame, mapExtent, randomColor);

            /* create new delegates instances and bind them to MapFrame's MapLayer managment methods */
            /*MapLayersManager.MapLayerAddedDelegate delegate1 = new MapLayersManager.MapLayerAddedDelegate(mFrame.MapLayerAdded);
            MapLayersManager.MapLayerDeletedDelegate delegate2 = new MapLayersManager.MapLayerDeletedDelegate(mFrame.MapLayerDeleted);
            MapLayersManager.Instance.MapLayerAdded += delegate1;
            MapLayersManager.Instance.MapLayerDeleted += delegate2;*/

            mFrame.RelatedBookmark = bookmark;
            mFrame.Orientation = frameOri;
            mFrame.Height = frameHeight;
            mFrame.Width = frameWidth;
            mFrame.Center = frameCenter;

            bookmark.MapFrameCurrentlyOpen = true;

            return mFrame;
        }

        #endregion

        #region utility methods
        /// <summary>
        /// Randomly generates a colour for a new
        /// MapFrame
        /// </summary>
        /// <returns></returns>
        private SolidColorBrush GetRandomColor()
        {
            /* 
             * Randomly generate a color
             * 
             * byte r = represents the red byte
             * byte g = the green byte
             * byte b = blue byte
             */
            Random rnd = new Random();
            byte r = Convert.ToByte(rnd.Next(0, 192));
            byte g = Convert.ToByte(rnd.Next(0, 192));
            byte b = Convert.ToByte(rnd.Next(0, 192));
            return new SolidColorBrush(Color.FromRgb(r, g, b));
        }
        #endregion
    }
}
